package edu.unc.ils.memai.vocab;

import java.io.File;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import javax.xml.namespace.QName;

import org.openrdf.concepts.skos.core.Concept;
import org.openrdf.elmo.ElmoModule;
import org.openrdf.elmo.sesame.SesameManager;
import org.openrdf.elmo.sesame.SesameManagerFactory;
import org.openrdf.repository.Repository;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.sail.nativerdf.NativeStore;

import edu.unc.ils.memai.extract.Utils;


/**
 * Modification of Medelyan's original Maui Vocabulary
 */

public class MauiVocabulary extends Vocabulary  
{
	/** index : descriptor --> id */
	private HashMap<String, String> termIdIndex;

	/** reverse index : id --> descriptor */
	private HashMap<String, String> idTermIndex;

	/** normalized descriptor --> list of all possible meanings */
	private HashMap<String, Vector<String>> listsOfSenses;

	/** non-descriptor id  --> descriptors id */
	private HashMap<String, String> nonDescriptorIndex = null;

	/** id -->  list of related ids */
	private HashMap<String, Vector<String>> listsOfRelatedTerms = null;

	/** id-relatedId --> relation */
	private HashMap<String, String> relationIndex = null;

	/**
	 * Builds the vocabulary indexes from SKOS file.
	 */
	public void load(String path) throws Exception {
		
		if (debugMode) {
			System.err.println("--- Indexing vocabulary from Sesame...");
		}	
		
		termIdIndex = new HashMap<String, String>();
		idTermIndex = new HashMap<String, String>();
		listsOfSenses = new HashMap<String, Vector<String>>();

		nonDescriptorIndex = new HashMap<String, String>();
		listsOfRelatedTerms = new HashMap<String, Vector<String>>();
		relationIndex = new HashMap<String, String>();
		
        // Open the Sesame repository
        String indexes = "spoc,ospc";
        NativeStore store = new NativeStore(new File(path), indexes);
        Repository repository = new SailRepository(store);
        repository.initialize();
        ElmoModule module = new ElmoModule();
        SesameManagerFactory factory = new SesameManagerFactory(module, repository);
        SesameManager manager = factory.createElmoManager(); 
        
        
        // to create IDs for non-descriptors!
        int count = 0;
        for (Concept concept:  manager.findAll(Concept.class))
        {
            QName qname = concept.getQName();
            String id = qname.getNamespaceURI() + qname.getLocalPart();
            
            // Pref label
            String prefLabel = concept.getSkosPrefLabel();

            String descriptorNormalized = normalizePhrase(prefLabel);

            if (descriptorNormalized.length() >= 1) {
                Vector<String> ids = listsOfSenses
                        .get(descriptorNormalized);
                if (ids == null)
                    ids = new Vector<String>();
                ids.add(id);
                listsOfSenses.put(descriptorNormalized, ids);

                termIdIndex.put(prefLabel.toLowerCase(), id);
                idTermIndex.put(id, prefLabel);
            }
                     
            // Alt labels
            Set<String> altLabels = concept.getSkosAltLabels();
            for (String altLabel: altLabels)
            {
                addNonDescriptor(count, id, altLabel);
                count++;
            }
            
            // Alt labels
            Set<String> hiddenLabels = concept.getSkosHiddenLabels();
            for (String hiddenLabel: hiddenLabels)
            {
                addNonDescriptor(count, id, hiddenLabel);
                count++;
            }
            
            /*
            // Scope notes
            Set<Object> scopeNotes = concept.getSkosScopeNotes();
            for (Object scopeNote: scopeNotes)
            {
            }
            */
            
            Set<Concept> broaders = concept.getSkosBroaders();
            for (Concept broader: broaders)
            {
                String relation = "broader";
                QName qn = broader.getQName();
                
                String relatedId = qn.getLocalPart();
               
                Vector<String> relatedIds = listsOfRelatedTerms.get(id);
                if (relatedIds == null)
                    relatedIds = new Vector<String>();

                relatedIds.add(relatedId);

                listsOfRelatedTerms.put(id, relatedIds);

                relationIndex.put(id + "-" + relatedId, relation);
            }
            
            Set<Concept> narrowers = concept.getSkosNarrowers();
            for (Concept narrower: narrowers)
            {
                String relation = "narrower";
                QName qn = narrower.getQName();
                
                String relatedId = qn.getLocalPart();
               
                Vector<String> relatedIds = listsOfRelatedTerms.get(id);
                if (relatedIds == null)
                    relatedIds = new Vector<String>();

                relatedIds.add(relatedId);

                listsOfRelatedTerms.put(id, relatedIds);

                relationIndex.put(id + "-" + relatedId, relation);
            }
            
            Set<Concept> relateds = concept.getSkosRelated();
            for (Concept related: relateds)
            {
                String relation = "related";
                QName qn = related.getQName();
                
                String relatedId = qn.getLocalPart();
               
                Vector<String> relatedIds = listsOfRelatedTerms.get(id);
                if (relatedIds == null)
                    relatedIds = new Vector<String>();

                relatedIds.add(relatedId);

                listsOfRelatedTerms.put(id, relatedIds);

                relationIndex.put(id + "-" + relatedId, relation);
                
                relationIndex.put(relatedId + "-" + id, relation);
            }
        }

        repository.shutDown();
        store.shutDown();
		
	}

	private void addNonDescriptor(int count, String idDescriptor,
			String nonDescriptor) {

		String idNonDescriptor = "d_" + count;
		count++;

		String normalizedNonDescriptor = normalizePhrase(nonDescriptor);
		if (normalizedNonDescriptor.length() >= 1) {
			Vector<String> ids = listsOfSenses.get(normalizedNonDescriptor);
			if (ids == null)
				ids = new Vector<String>();
			ids.add(idNonDescriptor);
			listsOfSenses.put(normalizedNonDescriptor, ids);
		}

		termIdIndex.put(nonDescriptor.toLowerCase(), idNonDescriptor);
		idTermIndex.put(idNonDescriptor, nonDescriptor);

		nonDescriptorIndex.put(idNonDescriptor, idDescriptor);
	}



	/**
	 * Returns the id of the given term
	 * @param phrase
	 * @return term id
	 */
	public String getID(String phrase) {
		String id = termIdIndex.get(phrase.toLowerCase());
		if (id != null) {
			if (nonDescriptorIndex.containsKey(id))
				id = nonDescriptorIndex.get(id);
		}
		return id;
	}

	/**
	 * Returns the term for the given id
	 * @param id - id of some phrase in the vocabulary
	 * @return phrase, i.e. the full form listed in the vocabulary
	 */
	public String getTerm(String id) {
		return idTermIndex.get(id);
	}

	/**
	 * Checks whether a normalized phrase 
	 * is a valid vocabulary term.
	 * @param phrase
	 * @return true if phrase is in the vocabulary
	 */
	public boolean containsNormalizedEntry(String phrase) {
		return listsOfSenses.containsKey(normalizePhrase(phrase));
	}

	/**
	 * Returns true if a phrase has more than one senses
	 * @param phrase
	 * @return false if a phrase has only one sense
	 */
	public boolean isAmbiguous(String phrase) {
		Vector<String> meanings = listsOfSenses.get(normalizePhrase(phrase));
		if (meanings == null || meanings.size() == 1) {
			return false;
		}
		return true;
	}

	/**
	 * Retrieves all possible descriptors for a given phrase
	 * @param phrase
	 * @return a vector list of all senses of a given term
	 */
	public List<String> getSenses(String phrase) {
		String normalized = normalizePhrase(phrase);

		List<String> senses = new ArrayList<String>();
		if (listsOfSenses.containsKey(normalized)) {
			for (String senseId : listsOfSenses.get(normalized)) {
				// 1. retrieve a descriptor if this sense is a non-descriptor
				if (nonDescriptorIndex.containsKey(senseId))
					senseId = nonDescriptorIndex.get(senseId);
				
				senses.add(senseId);
			}
		}
		return senses;

	}

	/**
	 * Given id of a term returns the list with ids of terms related to this term.
	 * @param id
	 * @return a vector with ids related to the input id
	 */
	public Vector<String> getRelated(String id) {
		return listsOfRelatedTerms.get(id);
	}

	/**
	 * Given an ID of a term gets the list of all IDs of terms
	 * that are semantically related to the given term
	 * with a specific relation
	 * @param id - id of some term in the vocabulary
	 * @param relation - a given semantic relation
	 * @return a vector with ids related to the input id by a specified relation
	 */
	public Vector<String> getRelated(String id, String relation) {
		Vector<String> related = new Vector<String>();
		Vector<String> all_related = listsOfRelatedTerms.get(id);
		if (all_related != null) {

			for (String rel_id : all_related) {
				String rel = relationIndex.get(id + "-" + rel_id);

				if (rel != null) {
					if (rel.equals(relation))
						related.add(rel_id);
				}
			}
		}
		return related;
	}

	/** 
	 * Generates the preudo phrase from a string.
	 * A pseudo phrase is a version of a phrase
	 * that only contains non-stopwords,
	 * which are stemmed and sorted into alphabetical order. 
	 */
	public String normalizePhrase(String phrase) {		
		return Utils.normalizePhrase(phrase, stemmer, stopwords, toLowerCase, reorder);
	}
}
